﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;

namespace Hydrogen.MathUtility
{
    public class MathExpressionEvaluator
    {
        private static readonly Dictionary<string, Func<double, double>> Functions =
            new Dictionary<string, Func<double, double>>(StringComparer.OrdinalIgnoreCase)
        {
            {"sin", Math.Sin}, {"cos", Math.Cos}, {"tan", Math.Tan},
            {"sqrt", Math.Sqrt}, {"log", Math.Log10}, {"ln", Math.Log},
            {"abs", Math.Abs}, {"exp", Math.Exp}, {"floor", Math.Floor},
            {"ceil", Math.Ceiling}, {"asin", Math.Asin}, {"acos", Math.Acos},
            {"atan", Math.Atan}
        };

        private static readonly Dictionary<string, double> Constants =
            new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase)
        {
            {"pi", Math.PI}, {"e", Math.E}
        };

        public static double Evaluate(string expression, Dictionary<string, double> variables = null)
        {
            expression = PreprocessExpression(expression);
            var tokens = Tokenize(expression);
            var rpn = ConvertToRPN(tokens);
            return EvaluateRPN(rpn, variables);
        }

        private static string PreprocessExpression(string expression)
        {
            // 移除所有空格并转换为小写
            expression = Regex.Replace(expression, @"\s+", "").ToLower();

            // 处理负数：将 "(-" 替换为 "(0-"
            expression = Regex.Replace(expression, @"\(-", "(0-");

            // 处理开头为负号的情况
            if (expression.StartsWith("-")) expression = "0" + expression;

            // 处理科学计数法
            expression = Regex.Replace(expression, @"(\d+\.?\d*)e([+-]?)(\d+)", match =>
            {
                double baseValue = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
                string expSign = match.Groups[2].Value;
                int exponent = int.Parse(match.Groups[3].Value);
                if (expSign == "-") exponent = -exponent;
                return (baseValue * Math.Pow(10, exponent)).ToString(CultureInfo.InvariantCulture);
            });

            // 处理乘号省略：在数字与字母/括号之间插入乘号
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < expression.Length; i++)
            {
                sb.Append(expression[i]);
                if (i < expression.Length - 1)
                {
                    char current = expression[i];
                    char next = expression[i + 1];

                    // 数字后跟字母或'('
                    if (char.IsDigit(current) && (char.IsLetter(next) || next == '('))
                    {
                        sb.Append('*');
                    }
                    // ')'后跟数字、字母或'('
                    else if (current == ')' && (char.IsDigit(next) || char.IsLetter(next) || next == '('))
                    {
                        sb.Append('*');
                    }
                    // 字母后跟数字或字母
                    else if (char.IsLetter(current) && (char.IsDigit(next) || char.IsLetter(next)))
                    {
                        sb.Append('*');
                    }
                }
            }
            return sb.ToString();
        }

        private static List<string> Tokenize(string expression)
        {
            List<string> tokens = new List<string>();
            Regex regex = new Regex(@"(?<function>[a-z]+)|(?<constant>pi|e)|(?<variable>[a-z][a-z0-9_]*)|(?<number>\d+\.?\d*)|(?<operator>[+\-*/%^(),])");
            MatchCollection matches = regex.Matches(expression);

            for (int i = 0; i < matches.Count; i++)
            {
                tokens.Add(matches[i].Value);
            }
            return tokens;
        }

        private static List<string> ConvertToRPN(List<string> tokens)
        {
            List<string> output = new List<string>();
            Stack<string> operators = new Stack<string>();

            Dictionary<string, int> precedence = new Dictionary<string, int>
            {
                {"+", 1}, {"-", 1},
                {"*", 2}, {"/", 2}, {"%", 2},
                {"^", 3}
            };

            for (int i = 0; i < tokens.Count; i++)
            {
                string token = tokens[i];

                if (double.TryParse(token, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
                {
                    output.Add(token);
                }
                else if (Constants.ContainsKey(token))
                {
                    output.Add(Constants[token].ToString(CultureInfo.InvariantCulture));
                }
                else if (Functions.ContainsKey(token))
                {
                    operators.Push(token);
                }
                else if (token == ",")
                {
                    while (operators.Count > 0 && operators.Peek() != "(")
                    {
                        output.Add(operators.Pop());
                    }
                }
                else if (precedence.ContainsKey(token))
                {
                    while (operators.Count > 0 &&
                           precedence.ContainsKey(operators.Peek()) &&
                           precedence[token] <= precedence[operators.Peek()])
                    {
                        output.Add(operators.Pop());
                    }
                    operators.Push(token);
                }
                else if (token == "(")
                {
                    operators.Push(token);
                }
                else if (token == ")")
                {
                    while (operators.Count > 0 && operators.Peek() != "(")
                    {
                        output.Add(operators.Pop());
                    }
                    if (operators.Count > 0 && operators.Peek() == "(")
                    {
                        operators.Pop();
                    }

                    if (operators.Count > 0 && Functions.ContainsKey(operators.Peek()))
                    {
                        output.Add(operators.Pop());
                    }
                }
                else // 处理变量
                {
                    output.Add(token); // 变量直接加入输出队列
                }
            }

            while (operators.Count > 0)
            {
                output.Add(operators.Pop());
            }

            return output;
        }

        private static double EvaluateRPN(List<string> rpn, Dictionary<string, double> variables)
        {
            Stack<double> stack = new Stack<double>();

            for (int i = 0; i < rpn.Count; i++)
            {
                string token = rpn[i];

                // 处理数字
                if (double.TryParse(token, NumberStyles.Any, CultureInfo.InvariantCulture, out double number))
                {
                    stack.Push(number);
                }
                // 处理常量
                else if (Constants.ContainsKey(token))
                {
                    stack.Push(Constants[token]);
                }
                // 处理函数
                else if (Functions.ContainsKey(token))
                {
                    double arg = stack.Pop();
                    stack.Push(Functions[token](arg));
                }
                // 处理变量
                else if (variables != null && variables.ContainsKey(token))
                {
                    stack.Push(variables[token]);
                }
                // 处理运算符
                else
                {
                    // 检查栈中是否有足够操作数
                    if (stack.Count < 2)
                    {
                        throw new InvalidOperationException($"Insufficient operands for operator: {token}");
                    }

                    double right = stack.Pop();
                    double left = stack.Pop();

                    switch (token)
                    {
                        case "+": stack.Push(left + right); break;
                        case "-": stack.Push(left - right); break;
                        case "*": stack.Push(left * right); break;
                        case "/":
                            if (right == 0) throw new DivideByZeroException("Division by zero");
                            stack.Push(left / right);
                            break;
                        case "%": stack.Push(left % right); break;
                        case "^": stack.Push(Math.Pow(left, right)); break;
                        default:
                            // 如果既不是运算符，尝试作为变量处理
                            if (variables != null && variables.ContainsKey(token))
                            {
                                stack.Push(left); // 恢复左操作数
                                stack.Push(variables[token]);
                            }
                            else
                            {
                                throw new KeyNotFoundException($"Undefined variable or function: {token}");
                            }
                            break;
                    }
                }
            }

            if (stack.Count != 1)
            {
                throw new InvalidOperationException("Invalid expression format");
            }

            return stack.Pop();
        }
    }
}